/* 
Parser for the C7 language
*/

%{
#include <iostream>
#include <string>
#include <map>
using namespace std;
void yyerror (const char *error);
%}

%union {
    float value;    /* For the lexical analyzer. NUMBER tokens */
    char *ident;  /* For the lexical analyzer. IDENT tokens */
}

%token <ident> IDENT
%token <value> NUMBER
%token ASSIGNOP
%token GT
%token GTE
%token LT
%token LTE
%token EQ
%token NEQ
%token PLUSEQ
%token MINUSEQ
%token AND
%token OR
%token IF
%token ELSE
%token FI
%token WHILE
%token ELIHW
%token FOR
%token ROF
%token FUN
%token NUF
%token RETURN
%token PRINT
%token INTTYPE
%token FLOATTYPE

%%

/* A program is a list of statements */
program: stmt_list 

/* A list of statements including function declaration */
stmt_list: stmt stmt_list      
| func_declare_stmt stmt_list 
| stmt                       
| func_declare_stmt         


/* A list of statements withou function declaration */
stmt_list_no_func_declare: stmt stmt_list_no_func_declare   
| stmt                                                     

/* A statement can be an assignment, arithmetic, declarative,
 * or branching / looping statement 
 */
stmt: assign_stmt ';'   
| arith_stmt ';'       
| declare_stmt ';'    
| expr ';'           
| PRINT '(' expr ')' ';' 
| if_stmt               
| while_stmt           
| for_stmt            


/* Assignment statement */
assign_stmt: IDENT ASSIGNOP expr 


/* Arithmetic assignment statements */
arith_stmt: IDENT PLUSEQ expr
| IDENT MINUSEQ expr 
| /*empty*/ 

block: block_stmt block    
| block_stmt              


/* A block statement differs from a normal statement by 
 * disallowing declaration, and including a return statement.
 * This will be used only inside conditional if/for/while 
 * statements.
 */
block_stmt: assign_stmt ';'
| arith_stmt ';'          
| RETURN expr ';'        
| expr ';'              
| PRINT '(' expr ')' ';' 
| if_stmt               
| while_stmt       
| for_stmt        

/*
 * Within the paranthesis inside 'for' only the below
 * statements are allowed.
 */
for_loop_stmt: assign_stmt 
| arith_stmt              
| expr                   

/* Variable declaration */
declare_stmt: INTTYPE declare_atom declare_args 
| FLOATTYPE declare_atom declare_args  

/* Declaring multiple variables. */
declare_args: ',' declare_atom declare_args
| /*empty*/ 

/* Variable declaration including assignment.*/
declare_atom: IDENT
| IDENT ASSIGNOP expr 

if_stmt: IF '(' expr ')' ELSE FI             
| IF '(' arith_stmt ')' ELSE FI            
| IF '(' assign_stmt ')' ELSE FI          
| IF '(' expr ')' block ELSE FI          
| IF '(' arith_stmt ')' block ELSE FI   
| IF '(' assign_stmt ')' block ELSE FI 
| IF '(' expr ')' ELSE block FI       
| IF '(' arith_stmt ')' ELSE block FI 
| IF '(' assign_stmt ')' ELSE block FI 
| IF '(' expr ')' block ELSE block FI 
| IF '(' arith_stmt ')' block ELSE block FI 
| IF '(' assign_stmt ')' block ELSE block FI 
| IF '(' expr ')' FI                        
| IF '(' arith_stmt ')' FI                 
| IF '(' assign_stmt ')' FI               
| IF '(' expr ')' block FI               
| IF '(' arith_stmt ')' block FI        
| IF '(' assign_stmt ')' block FI      

while_stmt: WHILE '(' expr ')' ELIHW   
| WHILE '(' arith_stmt ')' ELIHW      
| WHILE '(' assign_stmt ')' ELIHW    
| WHILE '(' expr ')' block ELIHW    
| WHILE '(' arith_stmt ')' block ELIHW
| WHILE '(' assign_stmt ')' block ELIHW  

for_stmt: FOR '(' for_loop_stmt ';' for_loop_stmt ';' for_loop_stmt ')' ROF 
| FOR '(' for_loop_stmt ';' for_loop_stmt ';' for_loop_stmt ')' block ROF  

/* 
 * Split expression statements into expr, term, factor to 
 * avoid shift and reduce conflicts
 */
expr: expr '+' term1        
| expr '-' term1           
| term1                   

term1: term1 '%' term2   
| term1 '/' term2       
| term1 '*' term2      
| term2               

term2: term2 AND factor 
| term2 OR factor      
| factor              

factor: '(' expr ')' 
| NUMBER            
| IDENT            
| func_call       
| IDENT EQ NUMBER  
| IDENT EQ IDENT  
| IDENT NEQ NUMBER 
| IDENT NEQ IDENT 
| IDENT GT NUMBER 
| IDENT GT IDENT 
| IDENT LT NUMBER
| IDENT LT IDENT 
| IDENT GTE NUMBER
| IDENT GTE IDENT  
| IDENT LTE NUMBER  
| IDENT LTE IDENT    

/* Declaration of a function. We check if there is atleast one 
 * return statement
 */
func_declare_stmt: FUN rettype func_start args_list ')' stmt_list_no_func_declare NUF 
| FUN rettype func_start args_list ')' RETURN expr ';' NUF  
| FUN rettype func_start args_list ')' stmt_list_no_func_declare RETURN expr ';' NUF  
| FUN rettype func_start args_list ')' stmt_list_no_func_declare RETURN expr ';' stmt_list_no_func_declare NUF  

/* Return type for a function */
rettype: INTTYPE  
| FLOATTYPE      

/* Argument list within a function declaration.*/
args_list: args  
| /* empty */   

args: INTTYPE IDENT   
| FLOATTYPE IDENT    
| INTTYPE IDENT ',' args 
| FLOATTYPE IDENT ',' args 

/* Statement representing the start of a function */
func_start : IDENT '('    

/* Function call statement */
func_call: func_start call_args_list ')'  

call_args_list: call_args   
| /* empty */              

call_args: expr           
| expr ',' call_args     

%%
